
#region Using directives

using System;
using System.Collections.Generic;

using FamilyTreeProject.Framework.Enums;
using FamilyTreeProject.Framework.Structures;
using FamilyTreeProject.Framework.Collections;
using FamilyTreeProject.Framework.Collections.Proxies;
using FamilyTreeProject.Data.Repositories;
using FamilyTreeProject.ComponentModel;

#endregion

namespace FamilyTreeProject.Framework.Entities
{
    /// <summary>
    /// The Individual Class models a Genealogical Family Record.
    /// </summary>
    public class Individual : EntityBase, IIndividual
    {

        #region Fields

        private DeathStatus deathStatus = DeathStatus.Unknown;
        private IIndividual father = null;
        private IIndividual mother = null;
        private Name name = new Name();
        private Sex sex = Sex.Unknown;

        private IObjectCollection<IIndividual> children;
        private IObjectCollection<IEvent> events;
        private IObjectCollection<IPersonalInfo> personalInfo;
        private IObjectCollection<IIndividual> spouses;

        #endregion

        #region Constructors

        /// <summary>
        /// Default Constructor, creates an Individual with a Null id (-1)
        /// </summary>
        protected internal Individual() : this(-1) { }

        /// <summary>
        /// Creates an Individual object from its Id
        /// </summary>
        /// <remarks>The collections created use the default "Repositories"</remarks>
        /// <param name="individualID">The id of the Individual</param>
        protected internal Individual(int individualID) 
            : base(individualID) 
        {
            CreateCollections();
        }

        /// <summary>
        /// Creates an Individual object from its Id
        /// </summary>
        /// <param name="individualID">The id of the Individual</param>
        /// <param name="repository">The IFamilyTreeRepository to use.</param>
        protected internal Individual(int individualID, IFamilyTreeRepository repository)
            : base(individualID, repository)
        {
            CreateCollections();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets a Collection of Children of the Individual
        /// </summary>
        public IObjectCollection<IIndividual> Children
        {
            get { return children; }
        }

        /// <summary>
        /// Gets or sets the DeathStatus of the Individual
        /// </summary>
        /// <value>A DeathStatus enum value</value>
        public DeathStatus DeathStatus
        {
            get { return deathStatus; }
            set { deathStatus = value; }
        }

        /// <summary>
        /// Gets the DisplayName for the Individual
        /// </summary>
        public string DisplayName
        {
            get { return Name.ToString(NameFormat.FirstLastLong); }
        }

        /// <summary>
        /// Gets a Collection of Events for the Individual
        /// </summary>
        public IObjectCollection<IEvent> Events
        {
            get { return events; }
            protected set { events = value; }
        }

        /// <summary>
        /// Gets or Sets the Father of the Individual
        /// </summary>
        public IIndividual Father
        {
            get { return father; }
            set { father = value; }
        }

        /// <summary>
        /// Gets or sets the Mother of the Individual
        /// </summary>
        public IIndividual Mother
        {
            get { return mother; }
            set { mother = value; }
        }

        /// <summary>
        /// Gets or sets the Name of the Individual
        /// </summary>
        /// <value>The Name of the Individual</value>
        public Name Name
        {
            get { return name; }
            set { name = value; }
        }

        /// <summary>
        /// Gets a Collection of PersonalInfo objects for the Individual
        /// </summary>
        public IObjectCollection<IPersonalInfo> PersonalInfo
        {
            get { return personalInfo; }
        }

        /// <summary>
        /// Gets or sets the Sex of the Individual
        /// </summary>
        /// <value>The Sex of the Individual</value>
        public Sex Sex
        {
            get { return sex; }
            set { sex = value; }
        }

        /// <summary>
        /// Gets the Spouses Collection
        /// </summary>
        public IObjectCollection<IIndividual> Spouses
        {
            get { return spouses; }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Creates the child collections of the Individual
        /// </summary>
        /// <param name="individualID">The Id of the indivdual</param>
        private void CreateCollections()
        {
            this.children = new ChildrenCollectionProxy(Id, CollectionOwnerType.Individual, Repository);
            this.events = new EventCollectionProxy(Id, CollectionOwnerType.Individual, Repository);
            this.personalInfo = new PersonalInfoCollectionProxy(Id, Repository);
            this.spouses = new SpouseCollectionProxy(Id, Repository);
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Calculates the Age of the Individual at a specific Date
        /// </summary>
        /// <param name="date">The date to use</param>
        /// <returns>An integer (-1 if cannot be determined)</returns>
        public int GetAge(Date date)
        {
            int retValue = -1;

            IEvent evnt = GetBirthEvent();
            if (evnt != null)
            {
                Date birthDate = evnt.Date.Date1;
                if (date.Year.HasValue && birthDate.Year.HasValue)
                    retValue = date.Year.Value - birthDate.Year.Value;

                if (date.Month.HasValue && birthDate.Month.HasValue)
                {
                    if (birthDate.Month.Value > date.Month.Value)
                    {
                        retValue--;
                    }
                    else if (birthDate.Month.Value == date.Month.Value)
                    {
                        if (date.Day.HasValue && birthDate.Day.HasValue)
                        {
                            if (birthDate.Day.Value > date.Day.Value)
                            {
                                retValue--;
                            }
                        }
                    }
                }
            }

            return retValue;
        }

        /// <summary>
        /// Returns the Birth (or Baptism)
        /// </summary>
        /// <returns>The Birth Event</returns>
        public IEvent GetBirthEvent()
        {
            IEvent evnt = null;
            Int32 i;

            // First Check if there is a Birth Event
            for (i = 0; i < Events.Count; i++)
            {
                evnt = Events[i];
                if (evnt.Type == EventType.Birth)
                {
                    //Cycle through Event Particpants to find the Principal Participant
                    foreach (IParticipant par in evnt.Participants)
                    {
                        if (par.IsOwner && par.Individual == this)
                        {
                            return evnt;
                        }
                    }
                }
            }
            // Next Check if there is a Christening Event
            for (i = 0; i < Events.Count; i++)
            {
                evnt = Events[i];
                if (evnt.Type == EventType.Christening)
                {
                    //Cycle through Event Particpants to find the Principal Participant
                    foreach (IParticipant par in evnt.Participants)
                    {
                        if (par.IsOwner && par.Individual == this)
                        {
                            return evnt;
                        }
                    }
                }
            }

            // Next Check if there is a Baptism Event
            for (i = 0; i < Events.Count; i++)
            {
                evnt = Events[i];
                if (evnt.Type == EventType.Baptism)
                {
                    //Cycle through Event Particpants to find the Principal Participant
                    foreach (IParticipant par in evnt.Participants)
                    {
                        if (par.IsOwner && par.Individual == this)
                        {
                            return evnt;
                        }
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Returns the Death (or Burial)
        /// </summary>
        /// <returns>The Death Event</returns>
        public IEvent GetDeathEvent()
        {
            IEvent evnt = null;
            Int32 i;

            // First Check if there is a Death Event
            for (i = 0; i < Events.Count; i++)
            {
                evnt = Events[i];
                if (evnt.Type == EventType.Death)
                {
                    //Cycle through Event Particpants to find the Principal Participant
                    foreach (IParticipant par in evnt.Participants)
                    {
                        if (par.IsOwner && par.Individual == this)
                        {
                            return evnt;
                        }
                    }
                }
            }
            // Next Check if there is a Burial Event
            for (i = 0; i < Events.Count; i++)
            {
                evnt = Events[i];
                if (evnt.Type == EventType.Burial)
                {
                    //Cycle through Event Particpants to find the Principal Participant
                    foreach (IParticipant par in evnt.Participants)
                    {
                        if (par.IsOwner && par.Individual == this)
                        {
                            return evnt;
                        }
                    }
                }
            }

            // Next Check if there is a Cremation Event
            for (i = 0; i < Events.Count; i++)
            {
                evnt = Events[i];
                if (evnt.Type == EventType.Cremation)
                {
                    //Cycle through Event Particpants to find the Principal Participant
                    foreach (IParticipant par in evnt.Participants)
                    {
                        if (par.IsOwner && par.Individual == this)
                        {
                            return evnt;
                        }
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// ToString overrides the default ToString method to output the name of the
        /// individual rather than its objectName
        /// </summary>
        /// <returns>The string representation of the Individual</returns>
        public override string ToString()
        {
            return ToString(NameFormat.LastFirstShort);
        }

        /// <summary>
        /// ToString overloads the default ToString method to output the name of the
        /// indiviual rather than its objectName
        /// </summary>
        /// <returns>The string representation of the Individual</returns>
        public string ToString(NameFormat format)
        {
            return Name.ToString(format);
        }

        #endregion

        #region IComparable Members

        /// <summary>
        /// Compares this instance with another Individual object
        /// </summary>
        /// <param name="obj">The Individual object to compare to</param>
        /// <returns>-ve, 0 or +ve</returns>
        public int CompareTo(object obj)
        {
            Int32 retValue = 0;

            if (obj is Individual)
            {
                Individual temp = (Individual)obj;

                //Compare based on the Name's
                retValue = name.CompareTo(temp.Name);

                if (retValue == 0)
                {
                    //Compare based on Birthdate
                    IEvent birth = GetBirthEvent();
                    IEvent compare = temp.GetBirthEvent();

                    if (birth == null && compare == null)
                    {
                        retValue = 0;
                    }
                    else if (birth == null)
                    {
                        retValue = +1;
                    }
                    else if (compare == null)
                    {
                        retValue = -1;
                    }
                    else
                        retValue = birth.CompareTo(compare);
                }
            }

            return retValue;
        }

        #endregion

    }
}
